#!/usr/bin/env python

# Author: Javier Cabezas <javier.cabezas@bsc.es>
#
# Copyright (c) 2013 Barcelona Supercomputing Center
#                    IMPACT Research Group
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import sys

import cudaprof
from cudaprof.common import enum
import cudaprof.cuda   as cuda
import cudaprof.io     as io

from datetime import datetime

def now():
    time = datetime.time(datetime.now())
    return time.strftime("%H:%M:%S")

# Used for debugging purposes
def shell():
    from IPython import embed
    embed()

ACTIONS = enum(TEMPLATE = 10,
               PROFILE  = 11)

def do_template(args):
    OPTION_CONF_FILE = args['tmpl_file']

    # Initialize CUDA
    cuda.init()

    # Create counters
    options = cuda.get_options()
    # Create counters
    counters = cuda.get_counters()
    # Create metrics
    metrics = cuda.get_metrics(counters)
    # Write to file
    io.put_conf_to_file(OPTION_CONF_FILE, options, counters, metrics)


def do_dependencies(args):
    OPTION_CONF_FILE = args['conf']

    # Initialize CUDA
    cuda.init()

    # Create counters
    options = cuda.get_options()
    # Create counters
    counters = cuda.get_counters()
    # Create metrics
    metrics = cuda.get_metrics(counters)
    # Read counters from configuration file
    options_conf, counters_conf, metrics_conf = io.get_conf_from_file(OPTION_CONF_FILE)

    # Merged options with configuration file
    cudaprof.init_options(options, options_conf)
    # Merged counters with configuration file
    cudaprof.init_counters(counters, counters_conf)
    # Merged metrics with configuration file
    cudaprof.init_metrics(metrics, metrics_conf)

    import cudaprof.gui.console as gui

    gui.start(options, counters, metrics, None, None, None, None, True)


def do_profile(args):
    OPTION_CMD       = args['cmdline']
    OPTION_CMD_ARGS  = ' '.join(args['args'])

    OPTION_GRAPHICAL = args['graphical']
    OPTION_CONF_FILE = args['conf']
    OPTION_OUT_FILE_PATTERN = args['out']

    # Initialize CUDA
    cuda.init()

    # Create counters
    options = cuda.get_options()
    # Create counters
    counters = cuda.get_counters()
    # Create metrics
    metrics = cuda.get_metrics(counters)
    # Read counters from configuration file
    options_conf, counters_conf, metrics_conf = io.get_conf_from_file(OPTION_CONF_FILE)

    # Merged options with configuration file
    cudaprof.init_options(options, options_conf)
    # Merged counters with configuration file
    cudaprof.init_counters(counters, counters_conf)
    # Merged metrics with configuration file
    cudaprof.init_metrics(metrics, metrics_conf)

    if not cuda.is_valid_output_pattern(OPTION_OUT_FILE_PATTERN):
        print 'Invalid output file pattern. Remember that it must contain the %d wilcard to generate one output file per GPU.'
        sys.exit(-1)

    if OPTION_GRAPHICAL == True:
        import cudaprof.gui.gtk as gui
    else:
        import cudaprof.gui.console as gui

    gui.start(options, counters, metrics, OPTION_CONF_FILE, OPTION_CMD, OPTION_CMD_ARGS, OPTION_OUT_FILE_PATTERN, False)


if __name__=="__main__":
    import argparse

    action = None
    def set_action(val):
        global action
        action = val

    parser = argparse.ArgumentParser(description = 'Profile CUDA programs')

    subparsers = parser.add_subparsers(title = 'subcommands',
                                       description = 'valid subcommands',
                                       help = 'sub-command help')

    parser_t = subparsers.add_parser('template',
                                     help = 'generate template configuration file')

    parser_t.add_argument('tmpl_file', metavar='CONF_FILE', type = str,
                          help = 'template configuration file to be generated')

    parser_t.set_defaults(func = do_template)

    parser_d = subparsers.add_parser('deps', help = 'Compute counter dependencies only')

    parser_d.add_argument('-c', '--conf', metavar='CONF_FILE', dest = 'conf', action='store',
                          default = None,
                          help = 'configuration file')

    parser_d.set_defaults(func = do_dependencies)

    parser_p = subparsers.add_parser('profile', help = 'profile the given program')

    parser_p.add_argument('cmdline', metavar='PROGRAM', type = str,
                          help = 'program to be profiled')
    parser_p.add_argument('args', metavar='ARG', type = str, nargs = '*',
                          help = 'argument to be passed to the program')
    parser_p.add_argument('-g', '--graphical', dest = 'graphical', action='store_const',
                          const = True, default = False,
                          help = 'show the graphical interface')
    parser_p.add_argument('-c', '--conf', metavar='CONF_FILE', dest = 'conf', action='store',
                          default = None,
                          help = 'configuration file')
    parser_p.add_argument('-o', '--out', metavar='OUT_FILE_PATTERN', dest = 'out', action='store',
                          default = 'cuda_profile_%d.log',
                          help = 'output file pattern')

    parser_p.set_defaults(func = do_profile)

    args = parser.parse_args()
    fun = args.func

    fun(vars(args))
